package aisLogic;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.Observable;
import java.io.File;
import java.io.FileOutputStream;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;

import org.w3c.dom.Node;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Attr;
import org.w3c.dom.NodeList;
import org.w3c.dom.NamedNodeMap;



public class Manager extends Observable
{
	public static final int ERROR = -1;
	
	public static final int ACTION = ERROR + 1;
	public static final int OBJECT = ACTION + 1;
	public static final int ARRAY_SIZE = OBJECT + 1;
	
	//////////////Module Constants////////////////
	
	public static final int MODULE_ADD = 0;
	public static final int MODULE_REMOVE = MODULE_ADD + 1;
	public static final int MODULE_UPDATE = MODULE_REMOVE + 1;
	public static final int MODULE_DOWN = MODULE_UPDATE + 1;
	public static final int MODULE_UP = MODULE_DOWN + 1;
	
	/////////////Frame Constants///////////////////
	
	public static final int FRAME_ADD = MODULE_UP + 1;
	public static final int FRAME_REMOVE = FRAME_ADD + 1;
	public static final int FRAME_CHANGE = FRAME_REMOVE + 1;
	public static final int FRAME_UP = FRAME_CHANGE + 1;
	public static final int FRAME_DOWN = FRAME_UP + 1;
	
	public static final int FRAME_ADD_MODULE_REF = FRAME_DOWN;
	public static final int FRAME_REMOVE_MODULE_REF = FRAME_ADD_MODULE_REF + 1;
	public static final int FRAME_REMOVE_MODULE_REF_MODULE = FRAME_REMOVE_MODULE_REF + 1; 
	public static final int FRAME_UPDATE_MODULE_REF_ATT_DX = FRAME_REMOVE_MODULE_REF_MODULE + 1; 
	public static final int FRAME_UPDATE_MODULE_REF_ATT_DY = FRAME_UPDATE_MODULE_REF_ATT_DX + 1;
	public static final int FRAME_UPDATE_MODULE_REF_ATT_MODULE = FRAME_UPDATE_MODULE_REF_ATT_DY + 1; 

	/////////////Frame Constants///////////////////
	
	public static final int ANIM_ADD = 11;
	public static final int ANIM_REMOVE = 12;
	public static final int ANIM_CHANGE = 13;
	public static final int ANIM_UPATE = 14;
	public static final int ANIM_DOWN = 15;
	public static final int ANIM_UP = 16;
	
	private LinkedList<Module> modules = new LinkedList<Module>();
	private LinkedList<Frame> frames = new LinkedList<Frame>();
	private LinkedList<Animation> anim = new LinkedList<Animation>();
	
	private boolean debug = false;
	
	
	///////////////////////////////////CLASS METHOD///////////////////////////////////////////
	
	public Manager()
	{
		super();
	}
	
	public void loadFromFile(File file)
	{
		System.out.println("load");
		/// aca va la carga del xml
	}
	
	public void saveTo(String filename)
	{
		String header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
		try
		{
		
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			Document newDocument = builder.newDocument();
			Element aisNode = newDocument.createElement("ais");
			
			this.fillImagesNode(aisNode);
			this.fillModulesNode(aisNode);
			this.fillFramesNode(aisNode);
			this.fillAnimationsNode(aisNode);
			
			byte[] byteArray;
			FileOutputStream stream = new FileOutputStream(filename);
			byteArray = header.getBytes();
			stream.write (byteArray,0,byteArray.length);
			
			byteArray = (aisNode.toString()).getBytes();
			stream.write (byteArray, 0, byteArray.length);
			stream.flush();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
	
	public Object[] notifyArray(int action, Object o)
	{
		Object[] ao = new Object[ARRAY_SIZE];
		ao[ACTION] = new Integer(action);
		ao[OBJECT] = o;
		return ao;
	}
	
	public void DEBUG(String s)
	{
		if(this.debug)
		{
			System.out.println(s);
		}
	}
	
	
	
	/////////////////////////////////////////MODULES/////////////////////////////////////////
	
	public void addModule(Module m)
	{
		this.modules.add(m);
		this.setChanged();
		this.notifyObservers(notifyArray(MODULE_ADD,m));
	}
	
	public int getModuleId(Module m)
	{
		int id = ERROR;
		for(int i = 0; i < this.modules.size(); i++)
		{
			Module curr = (Module)this.modules.get(i);
			if(curr == m) id = i;
		}
		return id;
	}
	
	public Module getModule(int index)
	{
		return (Module) this.modules.get(index);
	}
	
	public void removeModule(int index)
	{
		Module m = (Module)this.modules.remove(index);
		for(int i = 0; i < frames.size(); i++)
		{
			Frame f = (Frame)frames.get(i);
			f.removeModule(m);
		}
		this.setChanged();
		this.notifyObservers(notifyArray(MODULE_REMOVE, m));
	}
	
	public void moduleUpdate(int index, int moduleAtt, int att)
	{
		Module m = getModule(index);
		switch(moduleAtt)
		{
			case Module.ATT_X: 
				m.setX(att);
				break;
			case Module.ATT_Y:
				m.setY(att);
				break;
			case Module.ATT_HEIGHT:
				m.setHeight(att);
				break;
			case Module.ATT_WIDTH:
				m.setWidth(att);
				break;
			case Module.ATT_FLIP:
				m.setFlip(att);
			case Module.ATT_IMAGE_ID:
				m.setImageId(att);
				break;
			default:
				return;
		}
		this.setChanged();
		this.notifyObservers(notifyArray(MODULE_UPDATE, m));
	}
	
	public void moduleUpdate(int index, int moduleAtt, String att)
	{
		Module m = getModule(index);
		if(moduleAtt == Module.ATT_MISC)
		{
			m.setMisc(att);
			this.setChanged();
			this.notifyObservers(notifyArray(MODULE_UPDATE, m));
		}
	}
	
	public Module[] getModules(int from, int to)
	{
		if(to > this.modules.size()) return null;
		
		Module[] m = new Module[to - from];
		for (int i = 0; i < (to - from); i++)
		{
			m[i] = this.getModule(to +i);
		}
		return m;
	}
	
	public Module[] getAllModules()
	{
		return this.getModules(0, this.modules.size());
	}
	
	public int getSizeModuleList()
	{
		return this.modules.size(); 
	}
	
	public void moduleDown(int index)
	{
		DEBUG("moduleDown Antes : "+this.modules.size());
		if(index > this.modules.size()) return;
		Module m = (Module)this.modules.remove(index);
		DEBUG("moduleDown Durante:"+this.modules.size());
		this.modules.add(++index, m);
		this.setChanged();
		this.notifyObservers(notifyArray(MODULE_DOWN, m));
		DEBUG("moduleDown Despues : "+this.modules.size());
	}
	
	public void moduleUp(int index)
	{
		DEBUG("moduleDown Antes : "+this.modules.size());
		if(index <= 0) return;
		Module m = (Module)this.modules.remove(index);
		DEBUG("moduleDown Durante :"+this.modules.size());
		this.modules.add(--index, m);
		this.setChanged();
		this.notifyObservers(notifyArray(MODULE_UP, m));
		DEBUG("moduleDown Despues : "+this.modules.size());
	}
	
	///////////////////////////////////////////FRAMES/////////////////////////////////////////
	
	public void addFrame(Frame f)
	{
		this.frames.add(f);
		this.setChanged();
		this.notifyObservers(this.notifyArray(FRAME_ADD, f));
	}
	
	public Frame getFrame(int index)
	{
		return (Frame)this.frames.get(index);
	}
	
	public void removeFrame(int index)
	{
		Frame f = this.getFrame(index);
		this.frames.remove(index);
		this.setChanged();
		this.notifyObservers(this.notifyArray(FRAME_REMOVE, f));
	}
	
	public int getSizeFrameList()
	{
		return this.frames.size();
	}
	
	public Frame[] getFrames(int from, int to)
	{
		if(to > this.frames.size()) return null;
		Frame[] f = new Frame[to - from];
		for(int i = 0; i < (to - from); i++)
		{
			f[i] = this.getFrame(to + i);
		}
		return f;
	}
	
	public int getFrameId(Frame f)
	{
		int id = ERROR;
		for(int i = 0; i < this.frames.size(); i++)
		{
			Frame curr = (Frame)this.frames.get(i);
			if(curr == f) id = i;
		}
		return id;
	}

	
		
	public Frame[] getAllFrames()
	{
		return this.getFrames(0, this.frames.size());
	}
	
	public void frameUp(int index)
	{
		if(index <=0) return;
		Frame f = (Frame) this.frames.remove(index);
		this.frames.add(--index, f);
		this.setChanged();
		this.notifyObservers(this.notifyArray(FRAME_UP, f));
	}
	
	public void frameDown(int index)
	{
		if(index >= this.frames.size()) return;
		Frame f = (Frame)this.frames.remove(index);
		this.frames.add(++index, f);
		this.setChanged();
		this.notifyObservers(this.notifyArray(FRAME_DOWN, f));
	}

	///////////////////////////////////////////ANIMS///////////////////////////////////////////

	public void addAnim(Animation a)
	{
		this.anim.add(a);
		this.setChanged();
		this.notifyObservers(this.notifyArray(ANIM_ADD, a));
	}
	
	public Animation getAnim(int index)
	{
		return (Animation) this.anim.get(index);
	}
	
	public void removeAnim(int index)
	{
		Animation a = this.getAnim(index);
		this.anim.remove(index);
		this.setChanged();
		this.notifyObservers(this.notifyArray(ANIM_REMOVE, a));
	}
	
	public int getSizeAnimList()
	{
		return this.anim.size();
	}
	
	public Animation[] getAnims(int from, int to)
	{
		if(to > this.anim.size()) return null;
		Animation[] a = new Animation[to - from];
		for(int i = 0; i < (to - from); i++)
		{
			a[i] = this.getAnim(to +i);
		}
		return a;
	}
	
	
	public Animation[] getAllAnims()
	{
		return this.getAnims(0, this.anim.size());
	}
	
	
	public void animUp(int index)
	{
		if(index >= this.anim.size()) return;
		Animation a = (Animation)this.anim.remove(index);
		this.anim.add(++index,a);
		this.setChanged();
		this.notifyObservers(this.notifyArray(ANIM_UP, a));
	}
	
	
	public void animDown(int index)
	{
		if(index <= 0) return;
		Animation a = (Animation)this.anim.remove(index);
		this.anim.add(--index,a);
		this.setChanged();
		this.notifyObservers(this.notifyArray(ANIM_DOWN, a));
	}
	
	
	public void fillImagesNode(Element node)
	{
//		<images>
//			<image id="0" file="image.png"/>
//		</images>
		;;
	}
	

	//<modules>
	//	<module id="0" x="0" y="0" w="10" h="10" flip="0" misc="esto es misc" imageId="0"/>
	//	<module id="1" x="0" y="0" w="10" h="10" flip="5" misc="" imageId="0"/>
	//	<module id="2" x="0" y="0" w="10" h="10" flip="7" misc="null" imageId="0"/>
	//</modules>
	
	public void fillModulesNode(Element node)
	{
		Document doc = node.getOwnerDocument();
		Element nodeModules = doc.createElement("modules");
		
		for (int i = 0; i < this.modules.size(); i++)
		{

			Element module = doc.createElement("module");
			NamedNodeMap attributes = module.getAttributes();
			
			Module m = (Module)this.modules.get(i);
			
			Attr aId = doc.createAttribute("id"); 
			Attr aX = doc.createAttribute("x");
			Attr aY = doc.createAttribute("y");
			Attr aW = doc.createAttribute("w");
			Attr aH = doc.createAttribute("h");
			Attr aFlip = doc.createAttribute("flip");
			Attr aMisc = doc.createAttribute("misc");
			Attr aImageId = doc.createAttribute("imageId");
			
			aId.setNodeValue(Integer.toString(i));
			aX.setNodeValue(Integer.toString(m.getX()));
			aY.setNodeValue(Integer.toString(m.getY()));
			aW.setNodeValue(Integer.toString(m.getWidth()));
			aH.setNodeValue(Integer.toString(m.getHeight()));
			aFlip.setNodeValue(Integer.toString(m.getFlip()));
			aMisc.setNodeValue(m.getMisc());
			aImageId.setNodeValue("0");	//TODO manejo de IDs de imagenes
			
			attributes.setNamedItem(aId);
			attributes.setNamedItem(aX);
			attributes.setNamedItem(aY);
			attributes.setNamedItem(aW);
			attributes.setNamedItem(aH);
			attributes.setNamedItem(aFlip);
			attributes.setNamedItem(aMisc);
			attributes.setNamedItem(aImageId);
			
			nodeModules.appendChild(module);
		}
		node.appendChild(nodeModules);
	}
	

	//<frames>
	//	<frame id="0">
	//		<moduleRef dx="10" dy="20" idModule="0"/>
	//		<moduleRef dx="10" dy="20" idModule="1"/>
	//		<moduleRef dx="10" dy="20" idModule="2"/>
	//		<moduleRef dx="10" dy="20" idModule="1"/>
	//		<moduleRef dx="10" dy="20" idModule="0"/>
	//	</frame>
	//</frames>
	
	public void fillFramesNode(Element node)
	{
		Document doc = node.getOwnerDocument();
		Element nodeFrames = doc.createElement("frames");
		
		for (int i = 0; i < this.frames.size(); i++)
		{
			Frame f = (Frame)this.frames.get(i);
			Element nodeFrame = doc.createElement("frame");
			NamedNodeMap attrs = nodeFrame.getAttributes();
			
			Attr aId = doc.createAttribute("id");
			aId.setNodeValue(Integer.toString(i));
			for (int j = 0; j < f.getSize(); j++)
			{
				ModuleRef mr = f.getModuleRef(j);
				
				Element moduleRef = doc.createElement("moduleRef");
				NamedNodeMap attRef = moduleRef.getAttributes();
				
				Attr aDx = doc.createAttribute("dx");
				Attr aDy = doc.createAttribute("dy");
				Attr aIdModule = doc.createAttribute("idModule");
				
				aDx.setNodeValue(Integer.toString(mr.getDx()));
				aDy.setNodeValue(Integer.toString(mr.getDy()));
				aIdModule.setNodeValue(Integer.toString(this.getModuleId(mr.getModule())));
				
				attRef.setNamedItem(aDx);
				attRef.setNamedItem(aDy);
				attRef.setNamedItem(aIdModule);
				
				nodeFrame.appendChild(moduleRef);
			}
			
			attrs.setNamedItem(aId);
			nodeFrames.appendChild(nodeFrame);
		}
		node.appendChild(nodeFrames);
	}


	//	<animations>
	//	<animation id="0">
	//		<frameRef dx="10" dy="20" idFrame="1"/>
	//		<frameRef dx="20" dy="21" idFrame="0"/>
	//		<frameRef dx="15" dy="20" idFrame="0"/>
	//		<frameRef dx="05" dy="02" idFrame="1"/>
	//	</animation>
	//</animations>
	
	
	public void fillAnimationsNode(Element node)
	{
		Document doc = node.getOwnerDocument();
		Element nodeAnims = doc.createElement("animations");

		for (int i = 0; i < this.anim.size(); i++)
		{
			Animation anim = (Animation) this.anim.get(i);
			Element nodeAnim = doc.createElement("animation");
			NamedNodeMap attrs = nodeAnims.getAttributes();
			
			Attr aId = doc.createAttribute("id");
			aId.setNodeValue(Integer.toString(i));
			
			for (int j = 0; j < anim.getSize(); j++)
			{
				FrameRef fr = anim.getFrameRef(j);
				
				Element frameRef = doc.createElement("frameRef");
				NamedNodeMap attRef = frameRef.getAttributes();
				
				Attr aDx = doc.createAttribute("dx");
				Attr aDy = doc.createAttribute("dy");
				Attr aIdFrame = doc.createAttribute("idFrame");
				
				aDx.setNodeValue(Integer.toString(fr.getDx()));
				aDy.setNodeValue(Integer.toString(fr.getDy()));
				aIdFrame.setNodeValue(Integer.toString(this.getFrameId(fr.getFrame())));
				
				attRef.setNamedItem(aDx);
				attRef.setNamedItem(aDy);
				attRef.setNamedItem(aIdFrame);
				
				nodeAnim.appendChild(frameRef);
			}
			
			attrs.setNamedItem(aId);
			nodeAnim.appendChild(nodeAnim);
		}
		
		node.appendChild(nodeAnims);
	}
	
}
